home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-08 | 16.1 KB | 452 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "elecCompletion.tcl"
- # created: 8/3/96 {12:06:40 pm}
- # last update: 5/12/97 {3:39:59 pm}
- # Author: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # modified by rev reason
- # -------- --- --- -----------
- # 8/3/96 VMD 1.0 original
- # 20/11/96 VMD 1.1 many, many improvements.
- # 24/2/97 VMD 1.2 added some support of trf's code, plus some fixes
- # 1/9/97 VMD 1.5 added 'completion::contraction' and improved g-elec.
- # 12/1/97 trf 1.6 added 'Tutorial Shell' stuff, bumped to 9.0b2
- # 12/2/97 trf 1.7 corrected corrections, bumped to 9.0b3
- # 4/12/97 VMD 1.8 various fixes, better tcl8 compatibility
- # ###################################################################
- ##
-
- alpha::extension elecCompletions 9.0b3 {
- alpha::package require elecBindings 9.0b2
- menu::insert mode items end "completionsTutorial" "editCompletions"
- # load completion code for a mode the first time that mode is used
- hook::register mode::init completion::load "*"
- namespace eval completion {}
- completion::initialise
- } maintainer {
- "Vince Darley" darley@fas.harvard.edu <http://www.fas.harvard.edu/~darley/>
- } uninstall this-file help {file "ElecCompletions Help"}
-
- proc completion::initialise {} {}
-
- namespace eval elec {}
-
- proc completion::load {} {
- global HOME
- foreach f [glob -nocomplain ${HOME}:Tcl:Completions:[modeALike]Completions*.tcl] {
- message "loading [file tail $f]…"
- namespace eval ::[modeALike]::Completion {}
- uplevel \#0 [list source $f]
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::cmd" --
- #
- # General purpose proc for extending a given command to its full extent
- # in a mode-dependent fashion. If we hit a unique match, we call
- # '${mode}completion::Electric'; if we can extend, we do so, and set things up
- # so the calling procedure '${mode}completion::Cmd' will be called if
- # the user tries to cmd-Tab again; if we don't recognise anything,
- # we return 0
- #
- # We normally use the list ${m}cmds to look for completions, but the
- # caller can supply a different name. This is useful to prioritise
- # lists, so we first call with one, then another,... I currently use
- # this feature for TeX-completions, in which I call with a second list,
- # containing fake commands, which expand into environments.
- # -------------------------------------------------------------------------
- ##
- proc completion::cmd { {cmd ""} {listExt "cmds"} {prematch ""}} {
- if ![string length $cmd] {
- set cmd [completion::lastWord]
- # if there's any whitespace in the command then it's no good to us
- if [containsSpace $cmd] { return 0 }
- }
-
- set m [modeALike]
- # do an electric if we already match exactly
- global ${m}electrics
- if [info exists ${m}electrics($cmd)] {
- return [completion ${m} Electric "${prematch}${cmd}"]
- }
- if { [set matches [completion::modeList $cmd ${m}${listExt}]] == 0 } {
- return 0
- } else {
- return [completion::matchUtil Cmd $cmd $matches $prematch]
- }
- }
-
- proc completion::matchUtil {proc what matches {prematch ""}} {
- if {$matches == ""} { return 0 }
- set match [completion::Find $what $matches]
- if [string length $match] {
- # we completed or cancelled, so move on
- completion::already error
- if { $match == 1 } {
- return 1
- } else {
- return [completion [modeALike] Electric "${prematch}${match}"]
- }
- } else {
- completion::already $proc
- return 1
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::ensemble" --
- #
- # Complete and do electrics for commands which have two parts separated
- # by a space. Very useful for Tcl's "string compare ..." etc.
- # -------------------------------------------------------------------------
- ##
- proc completion::ensemble {dummy} {
- set lastword [completion::lastTwoWords prevword]
- set prevword [string trim $prevword]
- set m [modeALike]
- global ${m}${prevword}cmds
- if [info exists ${m}${prevword}cmds] {
- return [completion::cmd $lastword "${prevword}cmds" "${prevword} "]
- } else {
- return 0
- }
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::modeList" --
- #
- # Given a 'cmd' prefix and the name of a list to search, that list
- # being stored in alphabetical order and starting/ending with
- # whitespace, this proc returns a list of all matches with 'cmd',
- # or '0' if there were none. Updated so works with arrays too (Nov'96)
- # -------------------------------------------------------------------------
- ##
- proc completion::modeList { cmd slist } {
- global [lindex [split $slist "\("] 0]
- # Find all matches as a list --- a v. clever trick if I say so myself
- set reg "\[ \n\r\t\]+(${cmd}\[^ \n\r\t\]*\[ \n\r\t\]+)+"
- if [regexp $reg [set "$slist"] matches] {
- return $matches
- } else {
- return 0
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::electric" --
- #
- # Given a command, and an optional list of defaults, check the command
- # is ok and if so try and insert an electric entry.
- # -------------------------------------------------------------------------
- ##
- proc completion::electric { {cmd ""} args } {
- set m [modeALike]
- if ![string length $cmd] {
- set cmd [completion::lastWord]
- # only check for space if we're doing it
- if [containsSpace $cmd] { return 0 }
- }
-
- return [eval [list elec::findCmd $cmd ${m}electrics] $args]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::contraction" --
- #
- # Complete and do electrics for commands which have two parts separated
- # by a apostrophe. Useful for making shortcuts to things. ex: s'c Tcl's
- # "string compare ..." etc.
- # -------------------------------------------------------------------------
- ##
- proc completion::contraction {dummy} {
- set lastword [completion::lastTwoWords hint]
- if {![regexp "'\$" $hint]} {return 0}
- append hint $lastword
- return [completion::electric $hint]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "elec::findCmd" --
- #
- # General purpose proc for extending a command in some predetermined
- # fashion (such as mapping 'for' to a template 'for (;;)…'). Mode specific
- # procedures may use this if desired. The given command is looked up in
- # the given array '$arrayn', and if there is an entry, some electric
- # procedure happens. By default, if an entry is '0', then '0' is returned
- # (which can be used by the calling procedure to take some other action,
- # usually more sophisticated such as TeX-ref- completion), and if the entry
- # is an integer corresponding to a list element of the list 'args', then
- # that element is inserted. In this case list elements start with '1'
- # (because zero has a special meaning). Template stops in the electric
- # completion are marked by pairs of bullets '••'. If there is any text
- # between the bullets, that can be used to inform the user of what ought to
- # go there. All strings must contain at least one such template stop, to
- # which the insertion point moves.
- #
- # '$arrayn' ought not to be a large array or this proc may be slow.
- # (we first look for an exact array element match $arrayn($cmd), but
- # if that fails we look for a glob'ed match)
- #
- # The array element may contain control sequences. These start with
- # '◊', and may be followed by:
- #
- # kill0 --- delete the string which triggered this template before
- # inserting anything.
- #
- # killN --- delete all except N characters of the string.
- #
- # N --- use the N'th element of 'args' for the template.
- #
- # [ --- the string must be evaluated first (usually triggering some proc
- # which perhaps interacts with the user a bit)
- #
- # » --- an indirection; use the template insertion corresponding to
- # the given text item instead.
- #
- # In order to provide backward compatiblity of this proc with any new
- # control sequences that may be developed, any 'unknown' control
- # sequence is just deleted, a package that deals with the new sequences
- # thus has to overide this proc in order to make the now sequences
- # functionality available.
- #
- # So, what are some of the possible future control sequences? Well, I've
- # played with;
- #
- # sequences bound to a stop
- #
- # « --- an extended prompt, provides a longer, more pedalogical explanation
- # for a stop that the curt, fill in 'xxx' in the statusline.
- # ¶ --- a name that acts as an index into an array of code snippets, so a
- # bit of code can be executed when visiting a stop, perhaps aiding
- # in filling in options, validating entries, or anything else that
- # makes sense.
- # ø --- marks a stop of such an obvious nature, that the marking of the
- # stop with a dot, or and in-text prompt is superflous. In fact, such
- # stops often have existing statements dragged into their position,
- # so leaving them unmarked has a speed advantage. Perhaps this
- # action is best toggled depending on a flag value.
- #
- # Any stop that falls in the above class, will occur after any regular
- # prompting text, and should trigger the removal of itself and any
- # other characters up until the occurrence of the stop ending bullet.
- # That can be acomplished in one of two ways, here with a regsub of this
- # form:
- # regsub -all {•([^◊]*)◊[^•]+•} <template> {•\1•} result
- # or by applying the regsub to the entire set of electrics for a mode
- # as soon as its completions are loaded. (first method implemented)
- #
- # sequences that occurr at the start of a template
- # and apply to the template as a whole
- #
- # < --- means that certain conditions that must be meet by the text
- # proceeding where this template is to be inserted must be met
- # before the insertion is allowed, (e.g. a tcl command must be
- # proceeded by whitespace, a [, a ", or eval for the insertion
- # to be syntactically correct and thus , allowable)
- #
- # Sequences in this class will have to be of a single character, as
- # will get rid of any unknown sequence by
- # resub {◊[^k0-9»\[]} [string range <template 0 [string first • <template>]] head
- # set <template> $head
- # append <template> rest
- #
- # Includes some fixes by Tom Fetherston
- # -------------------------------------------------------------------------
- ##
- proc elec::findCmd { cmd arrayn args } {
- if {[set action [elec::_findCmd $cmd $arrayn]] == ""} { return 0 }
- # we have the action; check for control sequences
- while {[string index $action 0] == "◊"} {
- # control sequence: kill, procedure or choice of default value?
- set action [string range $action 1 end]
- if { [string range $action 0 3] == "kill" } {
- set dpos [expr [getPos] - [string length $cmd] - [string index $action 4]]
- deleteText $dpos [getPos]
- regsub -all "kill" [string range $action 5 end] $cmd action
- } elseif {[string index $action 0] == "\[" } {
- set action [subst $action]
- } elseif {[string index $action 0] == "»" } {
- set key [string range $action 1 end]
- global $arrayn
- set text [set ${arrayn}($key)]
- set action "◊kill0${key}${text}"
- } elseif {([scan $action %d idx]) \
- && (![ catch {lindex $args [expr $idx-1]} act]) } {
- set action $act
- } else {
- if {[info commands [set proc elec::action::[string index $action 1]]] == $proc} {
- set action [$proc $action]
- } else {
- set action [string range $action 2 end]
- }
- }
- }
- # then, we pull out any "bulleted-stop control sequences" that are
- # unknown to this version of elec::findCmd -trf
- regsub -all {•([^◊]*)◊[^•]+•} $action {•\1•} action
- elec::Insertion $action
- # The idea here is to continue with other completions (return 0)
- # if the character before the insertion point is non white-space
- global wordBreakPreface
- if {![regexp $wordBreakPreface [lookAt [expr [getPos] -1]]]} {
- if [isSelection] {deleteText [getPos] [selEnd]}
- return 0
- } else {
- return 1
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "elec::_findCmd" --
- #
- # Find the electric command in the given array, or return ""
- # -------------------------------------------------------------------------
- ##
- proc elec::_findCmd {cmd arrayn} {
- global $arrayn
- if [info exists ${arrayn}($cmd)] {
- return [set "${arrayn}($cmd)"]
- } else {
- if {[string first "*" [set elec_ar [array names $arrayn]]] != -1 } {
- # some of the array matches are glob'ed; we must go one at a time
- foreach elec $elec_ar {
- if [string match $elec $cmd] {
- return [set "${arrayn}($elec)"]
- }
- }
- }
- }
- return ""
- }
-
- # just so we have one!
- set userCompletions(date) {◊kill0◊[lindex [mtime [now]] 0]}
-
- # ensure old version loaded:
- catch "completion::user"
- ##
- # -------------------------------------------------------------------------
- #
- # "completion::user" --
- #
- # A user completion is used for small
- # mode-independent snippets, like your email address, name etc.
- # For instance I have the following defined:
- #
- # set userCompletions(vmd) "◊kill0Vince Darley"
- # set userCompletions(www) "◊kill0<[icGetPref WWWHomePage]>"
- # set userCompletions(e-) "◊kill0<[icGetPref Email]>"
- #
- # Here '◊kill0' is a control sequence which means kill exactly what
- # I just typed before carrying out this completion.
- # -------------------------------------------------------------------------
- ##
- proc completion::user { {cmd ""} } {
- if ![string length $cmd] { set cmd [completion::lastWord] }
- if [containsSpace $cmd] { return 0 }
-
- return [elec::findCmd $cmd userCompletions]
- }
-
- proc mode::completionsTutorial {} {
- global HOME
- set f "${HOME}:Tcl:Completions:[modeALike] Tutorial"
- set files [glob -nocomplain $f*]
- if {[llength $files] == 1} {
- set fName [lindex $files 0]
- set mode [file::whichModeForWin "dummy[file extension $fName]"]
- set t [readFile $fName]
- new -n "*Tutorial shell*" -m $mode
- setWinInfo shell 1
- insertText $t
- unset t
- goto 0
- bind 0x32 vsp $mode
- } else {
- alertnote "No tutorial exists for this mode. Why don't you write one?"
- }
- }
-
- proc vsp {} {
- if {[win::Current] != "*Tutorial shell*"} {
- typeText "`"
- return
- }
- searchString "◊"
- goto [expr [getPos]+2]
- findAgain
- if [isSelection] {
- deleteText [getPos] [selEnd]
- # add the following to prevent the 'non-use' of a template from
- # messing up the next completion
- ring::clear
- }
- }
-
- proc mode::editCompletions {} {
- global HOME
- set f ${HOME}:Tcl:Completions:[modeALike]Completions.tcl
- if [catch {openFileQuietly $f}] {
- beep
- if {[askyesno "No completions exist for this mode. Do you want to create some?"] == "yes"} {
- set fd [open $f "w"]
- close $fd
- edit $f
- insertText {##
- # This file will be sourced automatically, immediately after
- # the _first_ time the file which defines its mode is sourced.
- # Use this file to declare completion items and procedures
- # for this mode.
- #
- # Some common defaults are included below.
- ##
-
- ##
- # These declare, in order, the names of the completion
- # procedures for this mode. The actual procedure
- # must be named '${mode}Completion::${listItem}', unless
- # the item is 'completion::*' in which case that actual
- # procedure is called. The procedure 'modeALike' will
- # map modes to similar modes so procs don't need to be
- # repeated. However each mode requires its own array entry
- # here.
- ##
- set completions(<mode>) {contraction completion::cmd Ensemble completion::electric Var}
-
- }\
- {# ◊◊◊◊ Data for <mode> completions ◊◊◊◊ #
-
- # cmds to be completed to full length (no need for short ones)
- set <mode>cmds { class default enum register return struct switch typedef volatile while }
- # electrics
- set <mode>electrics(for) " \{•start•\} \{•test•\} \{•increment•\} \{\r\t•body•\r\}\r••"
- set <mode>electrics(while) " \{•test•\} \{\r\t•body•\r\}\r••"
- # contractions
- set <mode>electrics(s'c) "◊»string compare"
- set <mode>electrics(s'f) "◊»string first"
- }}}
- }
-